package com.gitee.apanlh.util.algorithm.encrypt.asymmetric;

import com.gitee.apanlh.util.algorithm.encrypt.CipherMode;
import com.gitee.apanlh.util.algorithm.encrypt.Encrypt;
import com.gitee.apanlh.util.encode.Base64Type;
import com.gitee.apanlh.util.encode.Base64Utils;
import com.gitee.apanlh.util.encode.HexUtils;
import com.gitee.apanlh.util.encode.StrEncodeUtils;
import com.gitee.apanlh.util.valid.Assert;

import javax.crypto.Cipher;
import java.security.Key;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.spec.KeySpec;

/**
 * 	非对称算法加密/解密抽象类
 * 	<br>默认实现基本功能
 * 	TODO 增加base64自定义协议规则
 *
 * 	@author Pan
 * 	@see    BouncyCastleAsymmetricAbstract
 */
public abstract class AsymmetricAbstract implements Encrypt {
	
	/**	
	 * 	自定义加解密器
	 * 	<br>用于子类中实现指定算法
	 * 	
	 * 	@author Pan
	 * 	@param 	key			密钥
	 * 	@param 	iv			初始化向量
	 * 	@param 	cipherMode	加解密器模式
	 * 	@return	Cipher	
	 */
	public abstract Cipher initCipher(byte[] key, byte[] iv, CipherMode cipherMode);
	
	/**
	 * 	抽象方法，加解密过程的最终执行方法
	 * 	
	 * 	@author Pan
	 * 	@param 	cipherMode	加密器模式
	 * 	@param 	content		内容
	 * 	@return	byte[]
	 */
	public abstract byte[] doFinal(CipherMode cipherMode, byte[] content);
	
	/**
	 * 	抽象方法，用于初始化密钥对
	 * 	
	 * 	@author Pan
	 * 	@param 	keyLength	长度
	 * 	@return	KeyPair
	 */
	public abstract KeyPair initKeyPair(int keyLength);
	
	/**	
	 * 	获取Provider
	 * 	
	 * 	@author Pan
	 * 	@return	Provider
	 */
	public abstract Provider getProvider();
	
	/**	
	 * 	获取公钥抽象方法
	 * 	
	 * 	@author Pan
	 * 	@return	PublicKey
	 */
	public abstract PublicKey getPublicKey();
	
	/**	
	 * 	获取私钥抽象方法
	 * 	
	 * 	@author Pan
	 * 	@return	PrivateKey
	 */
	public abstract PrivateKey getPrivateKey();
	
	/**	
	 * 	获取公钥抽象方法
	 * 	<br>也可以实现单独生成的PublicKey方法
	 * 
	 * 	@author Pan
	 * 	@param 	publicKey	公钥字节数组
	 * 	@return	PublicKey
	 */
	public abstract Key getPublicKey(byte[] publicKey);
	
	/**	
	 * 	获取私钥抽象方法
	 * 	<br>也可以实现单独生成的PrivateKey方法
	 * 
	 * 	@author Pan
	 * 	@param 	privateKey	私钥字节数组
	 * 	@return	PrivateKey
	 */
	public abstract Key getPrivateKey(byte[] privateKey);
	
	/**	
	 * 	获取公钥抽象方法
	 * 	<br>也可以实现单独生成的PublicKey方法
	 * 
	 * 	@author Pan
	 * 	@param 	publicKey	公钥字节数组
	 * 	@return	PublicKey
	 */
	public abstract Key getPublicKey(KeySpec publicKey);
	
	/**	
	 * 	获取私钥抽象方法
	 * 	<br>也可以实现单独生成的PrivateKey方法
	 * 
	 * 	@author Pan
	 * 	@param 	privateKey	私钥字节数组
	 * 	@return	PrivateKey
	 */
	public abstract Key getPrivateKey(KeySpec privateKey);
	
	/**	
	 * 	生成key抽象方法
	 * 	<br>可根据不同cipher模式生成不同key种类
	 * 
	 * 	@author Pan
	 * 	@param 	publicKey			公钥
	 * 	@param 	privateKey			私钥
	 * 	@param 	cipherMode			加密器模式
	 * 	@return Key
	 */
	public abstract Key generateKey(byte[] publicKey, byte[] privateKey, CipherMode cipherMode);
	
	/**	
	 * 	生成key抽象方法
	 * 	<br>可根据不同cipher模式生成不同key种类
	 * 
	 * 	@author Pan
	 * 	@param 	publicKey			公钥
	 * 	@param 	privateKey			私钥
	 * 	@param 	cipherMode			加密器模式
	 * 	@return Key
	 */
	public abstract Key generateKey(KeySpec publicKey, KeySpec privateKey, CipherMode cipherMode);
	
	@Override
	public byte[] encrypt(byte[] content) {
		Assert.isNotEmpty(content);
		return doFinal(CipherMode.ENCRYPT, content);
	}
	
	@Override
	public byte[] encrypt(String content) {
		Assert.isNotEmpty(content);
		return encrypt(content.getBytes());
	}
	
	@Override
	public String encryptToHex(byte[] content) {
		return encryptToHex(content, true);
	}
	
	@Override
	public String encryptToHex(byte[] content, boolean toLowerCase) {
		Assert.isNotEmpty(content);
		return HexUtils.encode(doFinal(CipherMode.ENCRYPT, content), toLowerCase);
	}
	
	@Override
	public String encryptToHex(String content) {
		return encryptToHex(content, true);
	}
	
	@Override
	public String encryptToHex(String content, boolean toLowerCase) {
		Assert.isNotEmpty(content);
		return encryptToHex(content.getBytes(), toLowerCase);
	}
	
	@Override
	public byte[] encryptToBase64(byte[] content) {
		Assert.isNotEmpty(content);
		return Base64Utils.encode(doFinal(CipherMode.ENCRYPT, content));
	}

	@Override
	public byte[] encryptToBase64(byte[] content, Base64Type base64Type) {
		Assert.isNotEmpty(content);
		return Base64Utils.encode(doFinal(CipherMode.ENCRYPT, content), base64Type);
	}

	@Override
	public byte[] encryptToBase64(String content) {
		Assert.isNotEmpty(content);
		return encryptToBase64(content.getBytes());
	}

	@Override
	public byte[] encryptToBase64(String content, Base64Type base64Type) {
		Assert.isNotEmpty(content);
		return encryptToBase64(content.getBytes(), base64Type);
	}

	@Override
	public String encryptToBase64Str(byte[] content) {
		Assert.isNotEmpty(content);
		return Base64Utils.encodeToStr(doFinal(CipherMode.ENCRYPT, content));
	}

	@Override
	public String encryptToBase64Str(byte[] content, Base64Type base64Type) {
		Assert.isNotEmpty(content);
		return Base64Utils.encodeToStr(doFinal(CipherMode.ENCRYPT, content), base64Type);
	}

	@Override
	public String encryptToBase64Str(String content) {
		Assert.isNotEmpty(content);
		return encryptToBase64Str(content.getBytes());
	}

	@Override
	public String encryptToBase64Str(String content, Base64Type base64Type) {
		Assert.isNotEmpty(content);
		return encryptToBase64Str(content.getBytes(), base64Type);
	}

	@Override
	public byte[] decrypt(byte[] content) {
		Assert.isNotEmpty(content);
		return doFinal(CipherMode.DECRYPT, content);
	}
	
	@Override
	public byte[] decryptFromHex(String content) {
		Assert.isNotEmpty(content);
		return decrypt(HexUtils.decode(content));
	}
	
	@Override
	public String decryptFromHexStr(String content) {
		return StrEncodeUtils.utf8EncodeToStr(decryptFromHex(content));
	}
	
	@Override
	public byte[] decryptFromBase64(byte[] content) {
		return decryptFromBase64(content, Base64Type.RFC4648);
	}
	
	@Override
	public byte[] decryptFromBase64(byte[] content, Base64Type base64Type) {
		Assert.isNotEmpty(content);
		return doFinal(CipherMode.DECRYPT, Base64Utils.decode(content, base64Type));
	}
	
	@Override
	public byte[] decryptFromBase64(String content) {
		return decryptFromBase64(content, Base64Type.RFC4648);
	}
	
	@Override
	public byte[] decryptFromBase64(String content, Base64Type base64Type) {
		Assert.isNotEmpty(content);
		return doFinal(CipherMode.DECRYPT, Base64Utils.decode(content, base64Type));
	}
	
	@Override
	public String decryptFromBase64Str(byte[] content) {
		return decryptFromBase64Str(content, Base64Type.RFC4648);
	}
	
	@Override
	public String decryptFromBase64Str(byte[] content, Base64Type base64Type) {
		return StrEncodeUtils.utf8EncodeToStr(decryptFromBase64(content, base64Type));
	}
	
	@Override
	public String decryptFromBase64Str(String content) {
		return decryptFromBase64Str(content, Base64Type.RFC4648);
	}
	
	@Override
	public String decryptFromBase64Str(String content, Base64Type base64Type) {
		return StrEncodeUtils.utf8EncodeToStr(decryptFromBase64(content, base64Type));
	}
 }
